/**
 * This file is part of Lineage II Client Data Parser.
 * 
 * Lineage II Client Data Parser is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Lineage II Client Data Parser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Lineage II Client Data Parser.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.l2emuproject.io;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import net.l2emuproject.parser.ClanBaseParser;
import net.l2emuproject.parser.ClassInfoParser;
import net.l2emuproject.parser.CommandParser;
import net.l2emuproject.parser.CreditParser;
import net.l2emuproject.parser.MonsterSkillParser;
import net.l2emuproject.parser.ObscenityParser;
import net.l2emuproject.parser.RecipeParser;
import net.l2emuproject.parser.SkillParser;
import net.l2emuproject.parser.SystemMessageParser;
import net.l2emuproject.parser.base.DefaultParser;
import net.l2emuproject.redist.UnsupportedException;

import javolution.text.TextBuilder;

/**
 * @author Savormix
 *
 */
public abstract class FileManager
{
	private static final String				PATH_TO_CLIENT_FILES	= "client_files/";
	private static final String				PATH_TO_SERVER_FILES	= "server_files/";
	private static final String				PATH_TO_SIMPLE_FILES	= "simple_files/";
	private static final String				FILE_FORMAT[]			=
																	{ "dec-", ".dat" };
	private static final DefaultParser[]	VALIDATORS				=
																	{
			new ClanBaseParser(),
			new ClassInfoParser(),
			new CommandParser(),
			new CreditParser(),
			new MonsterSkillParser(),
			new ObscenityParser(),
			new RecipeParser(),
			new SkillParser(),
			new SystemMessageParser()								};
	/** Used for debugging/testing/implementation purposes only */
	public static boolean					WRITE_TO_DUMP			= false;
	/** Used for debugging/testing/implementation purposes only */
	public static String					SP_READ_OUTPUT			= "read.bin";
	/** Will not append <B>dec-</B> to filename */
	public static boolean					ALTERNATIVE_LOOKUP;
	/** Maximum size of a file that will be parsed */
	public static long						MAX_FILESIZE			= 1024 * 1024 * 96;

	/**
	 * Validates if a file exists and is a valid decrypted chronicle file.
	 * @param name File name
	 * @param chronicle Chronicle ID as described in help
	 * @return file exists and is a decrypted valid chronicle file
	 * @throws FileNotFoundException File does not exist/insufficient permissions to read
	 * @throws UnsupportedException an unsupported chronicle ID
	 */
	public static boolean validateFile(String name, int chronicle) throws FileNotFoundException, UnsupportedException
	{
		File f = new File(generatePath(name, chronicle));
		if (!f.exists())
			throw new FileNotFoundException("File " + f.getName() + " does not exist!");
		else if (!f.canRead())
			throw new FileNotFoundException("File " + f.getName() + " exists, but you have denied read permission!");
		if (f.length() > MAX_FILESIZE || f.length() > Integer.MAX_VALUE)
			throw new UnsupportedException("File too large to be loaded: " + f.getName());
		return true;
	}

	private static String generatePath(String type, String filename, int chronicle, boolean qualified) throws UnsupportedException
	{
		if (chronicle < 7 || chronicle > 11)
			throw new UnsupportedException();
		TextBuilder result = TextBuilder.newInstance();
		result.append(type);
		result.append(chronicle);
		result.append('/');
		if (!qualified && !ALTERNATIVE_LOOKUP)
			result.append(FILE_FORMAT[0]);
		result.append(filename);
		if (!qualified)
			result.append(FILE_FORMAT[1]);
		String s = result.toString();
		TextBuilder.recycle(result);
		return s;
	}

	private static String generatePath(String filename, int chronicle) throws UnsupportedException
	{
		return generatePath(PATH_TO_CLIENT_FILES, filename, chronicle, false);
	}

	/**
	 * Reads file into memory as a SPF. Bypasses all validation.
	 * @param name Raw file name
	 * @param chronicle Chronicle ID
	 * @return file contents as SafelyPackedFile
	 * @throws UnsupportedException Unsupported chronicle
	 * @throws IOException Failure reading file
	 */
	public static final SafelyPackedFile[] getSPFile(int chronicle, String... name) throws UnsupportedException, IOException
	{
		SafelyPackedFile[] result = new SafelyPackedFile[name.length];
		for (int i = 0; i < name.length; i++)
		{
			File f = new File(generatePath(name[i], chronicle));
			FileChannel in = new FileInputStream(f).getChannel();
			ByteBuffer data = ByteBuffer.allocate((int) f.length());
			while (data.position() < data.capacity())
				in.read(data);
			data.flip();
			result[i] = new SafelyPackedFile(name[i], data, chronicle);
		}
		return result;
	}

	/**
	 * Returns a parser as a validator instance.
	 * @param rawName filename without dec- or .dat
	 * @return a parser as a validator instance
	 * @throws UnsupportedException given file is not parsable by any parser
	 */
	public static final DefaultParser getValidator(String rawName) throws UnsupportedException
	{
		DefaultParser dp;
		for (int i = 0; i < VALIDATORS.length; i++)
		{
			dp = VALIDATORS[i];
			for (String file : dp.getNeededFiles())
				if (file.toLowerCase().equals(rawName.toLowerCase()))
					return dp;
		}
		throw new UnsupportedException(rawName + " is not a parsable file!");
	}

	/**
	 * Obtains a Writer instance for writing out to file.
	 * @param chronicle Chronicle ID
	 * @param qualifiedName fully qualified filename
	 * @param simple simple or server directory
	 * @return Writer to write file contents
	 * @throws UnsupportedException unsupported chronicle ID
	 * @throws IOException failed obtaining Writer
	 */
	public static final Writer getWriter(int chronicle, String qualifiedName, boolean simple) throws UnsupportedException, IOException
	{
		File f;
		if (simple)
			f = new File(generatePath(PATH_TO_SIMPLE_FILES, qualifiedName, chronicle, true));
		else
			f = new File(generatePath(PATH_TO_SERVER_FILES, qualifiedName, chronicle, true));
		if (!f.exists())
		{
			if (simple)
				new File(PATH_TO_SIMPLE_FILES + '/' + chronicle).mkdirs();
			else
				new File(PATH_TO_SERVER_FILES + '/' + chronicle).mkdirs();
			f.createNewFile();
		}
		return new BufferedWriter(new FileWriter(f));
	}

	/**
	 * Deletes the specified file when the application finishes execution.
	 * @param chronicle Chronicle ID
	 * @param qualifiedName fully qualified filename
	 * @param simple simple or server directory
	 */
	public static final void deleteEmptyFile(int chronicle, String qualifiedName, boolean simple)
	{
		try
		{
			File f;
			if (simple)
				f = new File(generatePath(PATH_TO_SIMPLE_FILES, qualifiedName, chronicle, true));
			else
				f = new File(generatePath(PATH_TO_SERVER_FILES, qualifiedName, chronicle, true));
			f.delete();
		}
		catch (UnsupportedException e)
		{
			// Couldn't care less
		}
	}
}
